package com.demo.io.bytestream;

import org.junit.jupiter.api.Test;

import java.io.*;

/**
 * @author wangning
 * @date 2023/12/21 11:34
 * @description 缓冲流 BufferedInputStream
 * 缓冲流作用
 * 缓冲输入流BufferedInputStream
 * 缓冲输出流BufferedOutputStream
 * 作用：提高读写速度。
 * 注：flush()方法的原理解释：缓冲区的大小很大，有几千个字节。这里举例设定缓冲区大小为20个字节，
 * 如果存放到缓冲区的数据刚好是20个字节，那么缓冲区的数据会自动执行写操作（write），例如将数据
 * 写入对应的FileOutputStream文件输出流中，并通过文件输出流写入文件；如果存入到缓冲区的数据只
 * 有10个字节，并没有存满，这时则不会触发write方法去执行写操作，就会导致我们没有将数据写入文件
 * 当中，就会出问题。因此，在缓冲区不满的时候，我们调用flush()方法进行缓冲区的强制清空。
 */
public class BufferedStreamTest {

    /**
     * test01与test02用来测试flush()方法的作用
     * 当不调用flush()的时候
     * 运行发现，buffered.txt中依然是空的，没有内容。这里就验证了缓冲流是将数据写入缓冲区中了，
     * 并没有再将缓冲区中的数据写入buffered.txt中，这说明缓冲区未满，没有自动触发write方法执行写操作。
     */
    @Test
    public void test01() {
        try {
            FileInputStream fis = new FileInputStream("src/main/resources/file/buffered.txt");
            FileOutputStream fos = new FileOutputStream("src/main/resources/file/buffered.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            bos.write(50);
            bos.write('a');
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 调用flush()方法
     */
    @Test
    public void test02() {
        try {
            FileInputStream fis = new FileInputStream("src/main/resources/file/buffered.txt");
            FileOutputStream fos = new FileOutputStream("src/main/resources/file/buffered.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            bos.write(50);
            bos.write('a');
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 不调用flush()方法，而调用close()方法
     * 如果flush与close方法都不调用而缓冲区未满，是不会执行写操作（write）的。
     * 当调用flush与close中的任何一个方法后，都会强制清空缓冲区中剩余的内容。
     */
    @Test
    public void test03() {
        try {
            FileInputStream fis = new FileInputStream("src/main/resources/file/buffered.txt");
            FileOutputStream fos = new FileOutputStream("src/main/resources/file/buffered.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            bos.write(50);
            bos.write('a');
//            bos.flush();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test04() {
        try {
            FileInputStream fis = new FileInputStream("src/main/resources/file/buffered.txt");
            FileOutputStream fos = new FileOutputStream("src/main/resources/file/buffered.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            bos.write(50);
            bos.write('a');
            bos.flush();
            System.out.println(bis.read());
            System.out.println((char) bis.read());
            fos.close();
            bos.close();
            fis.close();
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test05() {
        try {
            FileInputStream fis = new FileInputStream("src/main/resources/file/buffered.txt");
            FileOutputStream fos = new FileOutputStream("src/main/resources/file/buffered.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            long startTime = System.currentTimeMillis();
            bos.write(50);
            bos.write('a');
            bos.flush();
            System.out.println(bis.read());
            System.out.println((char) bis.read());
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - startTime);
            fos.close();
            bos.close();
            fis.close();
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 大文件读取
     * 当处理大文件时，使用适当的技术和方法可以提高读写性能和效率。下面是一些针对大文件读写的示例说明：
     * 在下面示例中，我们使用了BufferedInputStream来提高读取性能。通过使用合适大小的缓冲区，
     * 并循环读取数据块，可以减少与底层文件系统的交互次数，从而提高效率。
     */
    @Test
    public void test06() {
        try (FileInputStream fis = new FileInputStream("large_file.txt");
             BufferedInputStream bis = new BufferedInputStream(fis)) {
            byte[] buffer = new byte[8192]; // 缓冲区大小，根据需要调整
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                // 处理读取的数据
                // ...
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 大文件写入
     * 在下面示例中，我们使用了BufferedOutputStream来提高写入性能。通过使用缓冲区，
     * 可以将数据先写入缓冲区，然后一次性写入磁盘，减少了频繁的磁盘写入操作，提高了效率。
     */
    @Test
    public void test07() {
        try (FileOutputStream fos = new FileOutputStream("large_file.txt");
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            byte[] data = null;// 大文件的数据源，例如从其他文件或网络读取
            bos.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static final int BUFFER_SIZE = 8192;

    /**
     * 在下面示例中，我们使用 BufferedInputStream 来处理大文件的复制操作。首先，我们创建一个 BufferedInputStream 对象，
     * 将其包装在一个 FileInputStream 中，以便从源文件中读取数据。然后，我们创建一个 FileOutputStream 对象，用于将数据写入目标文件。
     * 在复制过程中，我们使用一个缓冲区 buffer 来临时存储读取的数据。通过循环读取数据并将其写入目标文件，
     * 直到读取到文件末尾（bytesRead 返回 -1）。
     * 使用 BufferedInputStream 的好处是它能够提供缓冲功能，从而减少实际的磁盘访问次数，提高读取文件的效率。
     * 通过适当调整 BUFFER_SIZE 的大小，可以根据实际情况平衡内存占用和读写性能。
     * 需要注意的是，在使用 BufferedInputStream 时，应始终使用 try-with-resources 语句来确保输入流的正确关闭，以避免资源泄漏。
     * 请注意，在处理大文件时，仍然需要关注内存和性能方面的问题。
     * 如果文件非常大（几个 GB 或更大），可能需要采用分块读取和写入的方式，并结合使用 BufferedInputStream 和
     * BufferedOutputStream 进行处理，以避免内存溢出和性能问题。
     */
    @Test
    public void test08() {
        String sourceFilePath = "E:/常用软件/系统镜像/CentOS-7-x86_64-DVD-2009.iso";
        String targetFilePath = "E:/常用软件/系统镜像/CentOS-7-x86_64-DVD-2009_copy.iso";

        try {
            // 用复制大文件来测试
            long startTime = System.currentTimeMillis();
            copyLargeFile(sourceFilePath, targetFilePath);
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - startTime);
            System.out.println("Large file copied successfully.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void copyLargeFile(String sourceFilePath, String targetFilePath) throws IOException {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFilePath));
             FileOutputStream fos = new FileOutputStream(targetFilePath)) {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }
    }


    /**
     * 下面是一个示例代码，演示如何使用分块读取和写入的方式处理文件，并利用缓冲流来提高性能
     */
    @Test
    public void test09() {
        String sourceFilePath = "E:/常用软件/系统镜像/CentOS-7-x86_64-DVD-2009.iso";
        String destinationFilePath = "E:/常用软件/系统镜像/CentOS-7-x86_64-DVD-2009_copy.iso";
        int bufferSize = 8192; // 缓冲区大小，可以根据需要进行调整

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFilePath), bufferSize);
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destinationFilePath), bufferSize)) {
            long startTime = System.currentTimeMillis();


            byte[] buffer = new byte[bufferSize];
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - startTime);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 使用缓冲流复制文件
     */
    @Test
    void test10() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\收藏的书籍\\图解设计模式.pdf"));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\收藏的书籍\\图解设计模式_副本.pdf"))) {
            int content;
            while ((content = bis.read()) != -1) {
                bos.write(content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("使用缓冲流复制PDF文件总耗时:" + (end - start) + " 毫秒");
    }

    /**
     * 不使用缓冲流复制文件
     * 此方法特别慢，建议不要尝试大文件，执行时间过长，需要等待
     */
    @Test
    void test11() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        String sourceFile = "src/main/resources/file/test.pdf";
        String targetFile = "src/main/resources/file/test_copy.pdf";
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(targetFile)) {
            int content;
            while ((content = fis.read()) != -1) {
                fos.write(content);
                fos.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("使用普通流复制PDF文件总耗时:" + (end - start) + " 毫秒");
    }


    @Test
    void test12() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src/main/resources/file/test.pdf"));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/main/resources/file/test_copy.pdf"))) {
            int len;
            byte[] bytes = new byte[4 * 1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("使用缓冲流复制PDF文件总耗时:" + (end - start) + " 毫秒");
    }

    @Test
    void test13() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        try (FileInputStream fis = new FileInputStream("src/main/resources/file/test.pdf");
             FileOutputStream fos = new FileOutputStream("src/main/resources/file/test_copy.pdf")) {
            int len;
            byte[] bytes = new byte[4 * 1024];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("使用普通流复制PDF文件总耗时:" + (end - start) + " 毫秒");
    }


}
